#include <assert.h>
#include <string.h>

#include "jtypes.h"
#include "AttributeInfo.h"
#include "ConstantPool.h"

#include "Log.h"

struct GenericAttributeInfo : public AttributeInfo
{
   GenericAttributeInfo(ConstantPool * cp, u2 attribute_name_index, FILE *f) 
   {
     this->attribute_name_index=attribute_name_index;
     assert(cp->isUtf8At(attribute_name_index));
     attribute_length=readUint32(f);
     info=new u1[attribute_length];
     fread(info,attribute_length,1,f);
     tag=aie_unknown;
   }

   virtual void dump(ConstantPool * cp)
   {
   }

   virtual u4 getSavedLength()
   {
     return attribute_length;
   }

   virtual void save(FILE *f,ConstantPool * cp)
   {
     //if(strcmp(cp->getCString(attribute_name_index),"LineNumberTable")==0) 
     //  return;
     LOG_SAVE("At %ld saving code attribute %d: '%s'\n",ftell(f),attribute_name_index,
        cp->getCString(attribute_name_index));
     writeUint16(f,attribute_name_index);
     //writeUint32(f,attribute_length);
     writeUint32(f,getSavedLength());
     fwrite(info,attribute_length,1,f);
   }
  
   u4 attribute_length;
   u1 *info;
};


u4 AttributeInfoCode::getSavedLength()
{
  u4 size;
  size=4+
    4+assembler->getSize()+
    2+n_exceptions*8+
    2;
  for(u2 i=0;i<attributes.size();i++) {
    size+=6+attributes[i]->getSavedLength();
  }

  return size;
}

void AttributeInfoCode::resetCode()
{
  assembler->resetCode();
  lineTable->resetTable();
}

void AttributeInfoCode::save(FILE *f,ConstantPool * cp)
{
  LOG_SAVE("At %ld saving code attribute %d: '%s'\n",ftell(f),attribute_name_index,
      cp->getCString(attribute_name_index));

  writeUint16(f,attribute_name_index);
  writeUint32(f,getSavedLength()); 
  writeUint16(f,max_stack);
  writeUint16(f,max_locals);

  assembler->save(f);

  writeUint16(f,n_exceptions);
  for(u2 i=0;i<n_exceptions;i++) {
    writeUint16(f,exceptions[i].start_pc);
    writeUint16(f,exceptions[i].end_pc);
    writeUint16(f,exceptions[i].handler_pc);
    writeUint16(f,exceptions[i].catch_type);
  }

  writeUint16(f,attributes.size());
  for(u2 i=0;i<attributes.size();i++) {
    attributes[i]->save(f,cp);
  }
}

AttributeInfoCode::AttributeInfoCode(ConstantPool * cp)
{
  tag=aie_code;
  attribute_name_index=cp->addUtf8("Code");

  max_stack=10;
  max_locals=10;
  
  n_exceptions=0;

  assembler=new Assembler();
}

AttributeInfoCode::AttributeInfoCode(ConstantPool * cp, u2 attribute_name_index, FILE *f)
{
  tag=aie_code;
  this->attribute_name_index=attribute_name_index;
  attribute_length=readUint32(f);

  max_stack=readUint16(f);
  max_locals=readUint16(f);

  assembler=new Assembler(f);

  n_exceptions=readUint16(f);
  exceptions=new exception[n_exceptions];

  for(u2 i=0;i<n_exceptions;i++) {
    exceptions[i].start_pc=readUint16(f);
    exceptions[i].end_pc=readUint16(f);
    exceptions[i].handler_pc=readUint16(f);
    exceptions[i].catch_type=readUint16(f);
  }

  u2 acount;
  acount=readUint16(f);

  for(u2 i=0;i<acount;i++) {
    AttributeInfo * ai;
    ai=AttributeInfoFactory::fromFile(cp,f);
    //if(strcmp(cp->getCString(ai->attribute_name_index),"LineNumberTable")==0) {
    if(ai->tag==aie_line_table)
      lineTable=(AttributeInfoLineNumberTable*)ai;
    attributes.push_back(ai);
  }

}

u2 AttributeInfoLineNumberTable::addr2Line(u2 addr)
{
  u2 i;
  for(i=0;i<table.size() && addr<table[i].start_pc;i++) 
    ;
  if(i>1 && i<table.size()) 
    return table[i].line_number;
  return -1;
}  

u4 AttributeInfoLineNumberTable::getSavedLength()
{
  return 2+4*table.size();
}

void AttributeInfoLineNumberTable::save(FILE *f, ConstantPool *cp)
{
  LOG_SAVE("At %ld saving line number table attribute %d: '%s' (%d entries)\n",ftell(f),attribute_name_index,
      cp->getCString(attribute_name_index),table.size());
  writeUint16(f,attribute_name_index);
  writeUint32(f,getSavedLength()); // TODO: this must recalcualted!!!
  writeUint16(f,table.size());
  for(u2 i=0;i<table.size();i++) {
    writeUint16(f,table[i].start_pc);
    writeUint16(f,table[i].line_number);
  }
}

AttributeInfoLineNumberTable::AttributeInfoLineNumberTable(ConstantPool *, u2 attribute_name, FILE *f)
{
  tag=aie_line_table;
  this->attribute_name_index=attribute_name;
  attribute_length=readUint32(f);

  u2 ltable_entries=readUint16(f);
  ltable entry;
  for(u2 i=0;i<ltable_entries;i++) {
    entry.start_pc=readUint16(f);
    entry.line_number=readUint16(f);
    table.push_back(entry);
  }
}

void AttributeInfoLineNumberTable::resetTable()
{
  table.clear();
  ltable entry;
  entry.start_pc=0;
  entry.line_number=1;
  table.push_back(entry);
}

AttributeInfo * AttributeInfoFactory::fromFile(ConstantPool * cp, FILE *f)
{
  u2 attribute_name;
  AttributeInfo * ai;

  attribute_name=readUint16(f);
  assert(cp->isUtf8At(attribute_name));
#ifdef ENABLE_LOG_LOAD
  long int at=ftell(f);
#endif
  if(strcmp(cp->getCString(attribute_name),"LineNumberTable")==0) {
    ai=new AttributeInfoLineNumberTable(cp,attribute_name,f);
  } else if(strcmp(cp->getCString(attribute_name),"Code")==0) {
    ai=new AttributeInfoCode(cp,attribute_name,f);
  } else {
    ai=new GenericAttributeInfo(cp,attribute_name,f);
  }
  return ai;
}

